package com.fstec.ks.master.impl;

import io.netty.channel.ChannelHandler;

import java.io.File;
import java.util.Date;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;

import com.fstec.ks.master.MasterClient;
import com.fstec.ks.master.bean.ExamRunInfo;
import com.fstec.ks.master.bean.RegistInfo;
import com.fstec.ks.master.bean.RoundBakBean;
import com.fstec.ks.master.bean.ServerRegBean;
import com.fstec.ks.master.queue.MessageQueue;
import com.fstec.ks.master.queue.QueueMessage;
import com.fstec.ks.master.queue.QueueMeta;
import com.sinotn.SinotnLogger;
import com.sinotn.error.MessageException;
import com.sinotn.gson.GsonFacade;
import com.sinotn.id.SeqUUIDGenerator;
import com.sinotn.ks.definition.Message;
import com.sinotn.ks.netty.MessageNotifier;
import com.sinotn.ks.netty.message.SimpleMessage;
import com.sinotn.util.FileUtil;

public class DefaultMasterClient implements MasterClient,InitializingBean {
	private MessageQueue messageQueue;
	private RegistInfo registInfo=new RegistInfo();
	private NettyClient longClient;
	private String handlerInitializer;
	private int idleSeconds=0;
	private boolean ssl=false;
	
	public void setHandlerInitializer(String handlerInitializer) {
		this.handlerInitializer = handlerInitializer;
	}

	public void setIdleSeconds(int idleSeconds) {
		this.idleSeconds = idleSeconds;
	}

	public void setSsl(boolean ssl) {
		this.ssl = ssl;
	}

	@Override
	public RegistInfo getRegistInfo() {
		return this.registInfo;
	}

	public void setMessageQueue(MessageQueue messageQueue) {
		this.messageQueue = messageQueue;
	}

	@Override
	public String regist(String ipAddr, int port, ServerRegBean serverRegBean) {
		String ipAddrOld=this.registInfo.getIpAddr();
		if(ipAddrOld==null||!ipAddr.equals(ipAddrOld)||port!=this.registInfo.getPort()){
			this.registInfo.setIpAddr(ipAddr);
			this.registInfo.setPort(port);
			this.initNettyClient(true);
		}else{
			this.initNettyClient(false);
		}
		
		try{
			long timestamp=System.currentTimeMillis();
			while(!this.longClient.isConnected()){
				try{
					Thread.sleep(300);
				}catch(Throwable e){}
				if((System.currentTimeMillis()-timestamp)>2000){
					throw new MessageException("无法建立与监控服务连接[ipADdr="+this.registInfo.getIpAddr()+";port="+this.registInfo.getPort()+"]");
				}
			}
			SimpleMessage sm=new SimpleMessage(101, this.longClient.nextToken());
			sm.setText(GsonFacade.toJson(serverRegBean));
			MessageNotifier notifier=this.longClient.send(sm);
			notifier.doWait(0, "发送运行状态");
			Message message=notifier.getResponse();
			if(message!=null && message.getType()==0){
				String registId=message.getText();
				this.registInfo.setRegistId(registId);
				this.registInfo.setRegistTime(new Date());
				// 通过连接成功事件调用登录服务
				this.longClient.onConnectSuccess();
				this.messageQueue.init(registId);
				return registId;
			} else {
				SinotnLogger.DEBUG.error("注册监控服务失败"+(message==null?"Null":message.getText()), sm.getText());
				throw new MessageException(message==null?"注册监控服务失败":message.getText());
			}
		}finally{
			this.registInfo.save();
		}
	}

	private void initNettyClient(boolean force) {
		if(force){
			if(this.longClient!=null){
				this.longClient.shutdown(null);
			}
		}else{
			if(this.longClient!=null && this.longClient.isConnected()){
				return;
			}
		}
		try{
			this.longClient=new NettyClient(this.registInfo.getIpAddr(), this.registInfo.getPort(), false);
			this.longClient.setMessageQueue(this.messageQueue);
			this.longClient.setRegistInfo(this.registInfo);
			Class<?> clazz=ClassUtils.forName(handlerInitializer,ClassUtils.getDefaultClassLoader());
			ChannelHandler handler=(ChannelHandler)BeanUtils.instantiate(clazz);
			if(this.idleSeconds>0 || this.ssl){
				BeanWrapperImpl beanWrapper=new BeanWrapperImpl(handler);
				if(this.idleSeconds>0){
					beanWrapper.setPropertyValue("readIdleSeconds", this.idleSeconds);
					beanWrapper.setPropertyValue("writeIdleSeconds", this.idleSeconds);
				}
				if(this.ssl){
					beanWrapper.setPropertyValue("ssl", this.ssl);
				}
			}
			this.longClient.setChannelHandler(handler);
			this.longClient.setKeepAlive(true);
			this.longClient.connect(false);
		} catch (Throwable e){
			this.longClient=null;
			SinotnLogger.DEBUG.error("初始化服务监控连接发生系统错误", e);
		}
	}

	@Override
	public void unregist() {
		this.registInfo.reset();
		if(this.registInfo.isConfig()){
			this.registInfo.save();
		}
		if(this.longClient!=null){
			this.longClient.shutdown(null);
			this.longClient=null;
		}
		if(this.messageQueue!=null){
			this.messageQueue.close(true);
		}
	}
	
	@Override
	public void backupServer(File serverBakFile) {
		QueueMessage message=new QueueMessage();
		message.setId(SeqUUIDGenerator.genSequenceUUID());
		message.setType(201);
		message.setBodyText(FileUtil.getCrc32Sign(serverBakFile));
		message.setFile(serverBakFile);
		message.setTimestamp(System.currentTimeMillis());
		this.messageQueue.send(message);
	}

	@Override
	public void sendExamInfo(ExamRunInfo examRunInfo) {
		QueueMessage message=new QueueMessage();
		message.setId(SeqUUIDGenerator.genSequenceUUID());
		message.setType(202);
		message.setBodyText(GsonFacade.toJson(examRunInfo));
		message.setTimestamp(System.currentTimeMillis());
		this.messageQueue.send(message);
	}

	@Override
	public void backupRound(RoundBakBean roundBakBean, File roundBakFile) {
		if(roundBakBean.getSign()==null){
			roundBakBean.setSign(FileUtil.getCrc32Sign(roundBakFile));
		}
		if(roundBakBean.getUploadTime()==0){
			roundBakBean.setUploadTime(System.currentTimeMillis());
		}
		QueueMessage message=new QueueMessage();
		message.setId(SeqUUIDGenerator.genSequenceUUID());
		message.setType(203);
		message.setBodyText(GsonFacade.toJson(roundBakBean));
		message.setFile(roundBakFile);
		message.setTimestamp(System.currentTimeMillis());
		this.messageQueue.send(message);
	}

	
	@Override
	public void afterPropertiesSet() throws Exception {
		this.registInfo.loadDt();
		if(this.registInfo.isRegist()){
			this.messageQueue.init(this.registInfo.getRegistId());
			this.initNettyClient(false);
		}
	}

	@Override
	public QueueMeta getQueueMeta() {
		if(this.messageQueue==null) return null;
		return this.messageQueue.getMeta();
	}

	@Override
	public boolean isConnect() {
		if(this.longClient==null) return false;
		return this.longClient.isActive();
	}

	@Override
	public void saveServerInfo(String ipAddr, int port) {
		if(this.isDiff(ipAddr, port)){
			this.registInfo.setIpAddr(ipAddr);
			this.registInfo.setPort(port);
			if(this.registInfo.isRegist()){
				this.initNettyClient(true);
			}
			this.registInfo.save();
		}
	}

	private boolean isDiff(String ipAddr, int port) {
		if(this.registInfo.getIpAddr()==null) return true;
		if(this.registInfo.getPort()==0) return true;
		if(!this.registInfo.getIpAddr().equals(ipAddr)) return true;
		return this.registInfo.getPort()!=port;
	}
}
